package views.fliper
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	public class FlipPageView extends Sprite
	{
		private var hSlice:int;
		private var vSlice:int;
		private var _flipRotation:Number = 0;
		
		public var asix:int;
		public var target:BitmapData;
		
		public function FlipPageView(hSlice:int = 2,vSlice:int = 20)
		{
			this.hSlice = hSlice;
			this.vSlice = vSlice;
		}
		
		
		public function initialize(target:BitmapData):void
		{
			
			this.target = target;
			calculatePoints(new Point(0,0),new Point(0,target.height),new Point(target.width,target.height),new Point(target.width,0));
		}
		
		public function reset():void
		{
			graphics.clear();
			
			if(this.target != null)
				this.target.dispose();
			
			_flipRotation = 0;
			
			if(target is BitmapData)
				target.dispose();
			target = null;
		}
		
		private function flipY(radian:Number):void
		{
			var resizeRect:Rectangle = new Rectangle(0,0,target.width,target.height);
			var vSizeChanged:Number = (resizeRect.height * 0.1) * (2 / Math.PI) * radian;
			var hSizeChanged:Number =  resizeRect.width * (2 / Math.PI) * radian;
			
			var tl:Point = new Point(resizeRect.x + hSizeChanged / 2, flipRotation < Math.PI / 2 ? resizeRect.y + vSizeChanged / 2 : resizeRect.y - vSizeChanged / 2);
			var bl:Point = new Point(resizeRect.x + hSizeChanged / 2, flipRotation < Math.PI / 2 ? resizeRect.bottom - vSizeChanged / 2 : resizeRect.bottom + vSizeChanged / 2);
			var br:Point = new Point(resizeRect.right - hSizeChanged / 2,flipRotation > Math.PI / 2 ? resizeRect.bottom - vSizeChanged / 2 : resizeRect.bottom + vSizeChanged / 2);
			var tr:Point = new Point(resizeRect.right - hSizeChanged / 2,flipRotation > Math.PI / 2 ? resizeRect.y + vSizeChanged / 2 : resizeRect.y - vSizeChanged / 2);
			
			var maxEdge:Number = Math.max(bl.y - tl.y,br.y - tr.y);
			if(maxEdge > resizeRect.height)
			{
				var extra:Number = (maxEdge - resizeRect.height) / 2;
				tl.y += extra;
				bl.y -= extra;
				tr.y += extra;
				br.y -= extra;
			}
			
			calculatePoints(tl,bl,br,tr);
		}
		
		private function flipX(radian:Number):void
		{
			var resizeRect:Rectangle = new Rectangle(0,0,target.width,target.height);
			var vSizeChanged:Number = resizeRect.height * (2 / Math.PI) * radian;
			var hSizeChanged:Number = (resizeRect.width * 0.1) * (2 / Math.PI) * radian;
			
			var tl:Point = new Point(flipRotation < Math.PI / 2 ? resizeRect.x + hSizeChanged / 2 : resizeRect.x - hSizeChanged / 2,resizeRect.y + vSizeChanged / 2);
			var tr:Point = new Point(flipRotation < Math.PI / 2 ? resizeRect.right - hSizeChanged / 2 : resizeRect.right + hSizeChanged / 2,resizeRect.y + vSizeChanged / 2);
			var bl:Point = new Point(flipRotation < Math.PI / 2 ? resizeRect.x - hSizeChanged / 2 : resizeRect.x + hSizeChanged / 2,resizeRect.bottom - vSizeChanged / 2);
			var br:Point = new Point(flipRotation < Math.PI / 2 ? resizeRect.right + hSizeChanged / 2 : resizeRect.right - hSizeChanged / 2,resizeRect.bottom - vSizeChanged / 2);
			
			var maxEdge:Number = Math.max(br.x - bl.x,tr.x - tl.x);
			if(maxEdge > resizeRect.width)
			{
				var extra:Number = (maxEdge - resizeRect.width) / 2;
				br.x -= extra;
				tr.x -= extra;
				bl.x += extra;
				tl.x += extra;
			}
			
			calculatePoints(tl,bl,br,tr);
		}
		
		
		public function set flipRotation(radian:Number):void
		{
			_flipRotation = radian;
			radian = radian > Math.PI / 2 ? Math.PI - radian : radian;
			
			if(asix == 0)
				flipX(radian);
			else if(asix == 1)
				flipY(radian);
		}
		
		public function get flipRotation():Number
		{
			return _flipRotation;
		}
		
		private function calculatePoints(lt:Point,lb:Point,rb:Point,rt:Point):void
		{
			var points:Vector.<Vector.<Point>> = new Vector.<Vector.<Point>>;
			for(var i:int = 0;i <= hSlice;i++)
			{
				var linePoints:Vector.<Point> = new Vector.<Point>;
				points.push(linePoints);

				for(var j:int = 0;j <= vSlice;j++)
				{
					var point:Point = new Point;
					
					var lineBeginX:Number = lt.x + (lb.x - lt.x) / hSlice * i;
					var lineEndX:Number = rt.x + (rb.x - rt.x) / hSlice * i;
					point.x = lineBeginX + (lineEndX - lineBeginX) / vSlice * j;
					
					var colBeginY:Number = lt.y + (rt.y - lt.y) / vSlice * j;
					var colEndY:Number = lb.y + (rb.y - lb.y) / vSlice * j;
					point.y = colBeginY + (colEndY - colBeginY) / hSlice * i;
					
					linePoints.push(point);
				}
			}
			drawGraphic(points);
		}
		
		private function drawGraphic(points:Vector.<Vector.<Point>>):void
		{
			graphics.clear();
			for(var i:int = 0;i < points.length - 1;i++)
			{
				for (var j:int = 0;j< points[i].length - 1;j++)
				{
					var tl:Point = points[i][j];
					var bl:Point = points[i+1][j];
					var br:Point = points[i+1][j+1];
					var tr:Point = points[i][j+1];

					var oriTL:Point = new Point(target.width / vSlice * j,target.height / hSlice * i);
					var oriBL:Point = new Point(target.width / vSlice * j,target.height / hSlice * (i + 1));
					var oriBR:Point = new Point(target.width / vSlice * (j + 1),target.height / hSlice * (i + 1));
					var oriTR:Point = new Point(target.width / vSlice * (j + 1),target.height / hSlice * i);
					
					var triangle1:Vector.<Point> = new Vector.<Point>;
					triangle1.push(tl,bl,tr);
					var oriTriangle1:Vector.<Point> = new Vector.<Point>;
					oriTriangle1.push(oriTL,oriBL,oriTR);
					
					var triangle2:Vector.<Point> = new Vector.<Point>;
					triangle2.push(tr,bl,br);
					var oriTriangle2:Vector.<Point> = new Vector.<Point>;
					oriTriangle2.push(oriTR,oriBL,oriBR);

					// draw triangle on top
					graphics.beginBitmapFill(target,getMatrix(triangle1,oriTriangle1),false,true);
					graphics.moveTo(tl.x,tl.y);
					graphics.lineTo(bl.x,bl.y);
					graphics.lineTo(tr.x,tr.y);
					graphics.lineTo(tl.x,tl.y);
					
					//draw triangle on bottom.
					graphics.beginBitmapFill(target,getMatrix(triangle2,oriTriangle2),false,true);
					graphics.moveTo(tr.x,tr.y);
					graphics.lineTo(bl.x,bl.y);
					graphics.lineTo(br.x,br.y);
					graphics.lineTo(tr.x,tr.y);
					
					graphics.endFill();
				}
			}
		}
		
		private function getMatrix(points:Vector.<Point>,oriPoints:Vector.<Point>):Matrix
		{
			var a:Number = ( (points[0].x-points[1].x)*(oriPoints[1].y-oriPoints[2].y) - (points[1].x-points[2].x)*(oriPoints[0].y-oriPoints[1].y) ) /
					  	   ( (oriPoints[0].x-oriPoints[1].x)*(oriPoints[1].y-oriPoints[2].y) - (oriPoints[1].x-oriPoints[2].x)*(oriPoints[0].y-oriPoints[1].y) );
			var c:Number = ( (points[0].x - points[1].x) - (oriPoints[0].x - oriPoints[1].x) * a) / (oriPoints[0].y - oriPoints[1].y);
			var tx:Number = points[0].x - c * oriPoints[0].y - a * oriPoints[0].x;
			
			var b:Number = ( (points[0].y-points[1].y)*(oriPoints[1].y-oriPoints[2].y) - (points[1].y-points[2].y)*(oriPoints[0].y-oriPoints[1].y) ) /
						   ( (oriPoints[0].x-oriPoints[1].x)*(oriPoints[1].y-oriPoints[2].y) - (oriPoints[1].x-oriPoints[2].x)*(oriPoints[0].y-oriPoints[1].y) );
			var d:Number = ( (points[0].y - points[1].y) - (oriPoints[0].x - oriPoints[1].x) * b) / (oriPoints[0].y - oriPoints[1].y);
			var ty:Number = points[0].y - d * oriPoints[0].y - b * oriPoints[0].x;
			
			
			return new Matrix(a,b,c,d,tx,ty);
		}
		
	}
}